#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <string.h>

#include "tda_sheet.h"
#include "tda_synth.h"
#include "tda_wav.h"
#include "common.h"
#include "ondamod.h"
#include "msg_esp.c"
#include "main.h"

const unsigned int frec[VALID_F_QTY]={8000,9600,11025,12000,16000,22050,24000,32000,44100,48000,88200,96000};

int main(int argc, char * argv[]){

	FILE * f1, * f2, * f3;
	adt_sheet_t * sheet;
	adt_synth_t * synth;
	adt_wav_t * wav;
	status_t status;
	int aux_start_time, notes_qty, samples_qty, int_limit;
	unsigned int sampling_frequency=0,s_arg_position=0, p_arg_position=0, o_arg_position=0;
	size_t n_note, sample_shift, sample;
	float it, t, n_length, decay_time, *aux_samples, sheet_time, aux_max_amp, aux_min_amp, amp_peak, amp_factor, period, aux_f;
	
	if((status=validate_args(argc,argv,&sampling_frequency,&s_arg_position,&p_arg_position,&o_arg_position))!=OK){
		return EXIT_FAILURE;
	}
	
	if((f1=fopen(argv[s_arg_position],"rt"))==NULL){
		fprintf(stderr,"%s %s\n",s_msg[error_file],argv[s_arg_position]);
		return EXIT_FAILURE;
	}
	
	if((f2=fopen(argv[p_arg_position],"rt"))==NULL){
		fclose(f1);
		fprintf(stderr,"%s %s\n",s_msg[error_file],argv[p_arg_position]);
		return EXIT_FAILURE;
	}
	
	if((f3=fopen(argv[o_arg_position],"wb"))==NULL){
		printf("%s %s\n",s_msg[error_file],argv[o_arg_position]);
		fclose(f1);
		fclose(f2);
		return EXIT_FAILURE;
	}
	
	if((status=adt_synth_new(&synth,f1))!=OK){
		
		fclose(f1);
		fclose(f2);
		fclose(f3);
		print_msg(status);
		if(status==ERROR_INVALID_DATA)fprintf(stderr,"%s\n",argv[s_arg_position]);
		return EXIT_FAILURE;
	}
	
	if((status=adt_sheet_new(&sheet,f2))!=OK){
		adt_synth_destroy(&synth);
		fclose(f1);
		fclose(f2);
		fclose(f3);
		print_msg(status);
		if(status==ERROR_INVALID_DATA)fprintf(stderr,"%s\n",argv[p_arg_position]);
		return EXIT_FAILURE;
	}

	

	decay_time=adt_synth_get_function_arg(synth, DECAY, 0);
	notes_qty=adt_sheet_get_notes_qty(sheet);
	sheet_time=adt_sheet_get_total_t(sheet);
	samples_qty=(int)ceil((sheet_time+decay_time)*sampling_frequency);
	
	if((aux_samples=(float *)malloc(samples_qty*sizeof(float)))==NULL){
		adt_sheet_destroy(&sheet);
		adt_synth_destroy(&synth);
		fclose(f1);
		fclose(f2);
		fclose(f3);
		print_msg(error_mem);
		return EXIT_FAILURE;
	}

	period=(1/(float)sampling_frequency);
	
	for (n_note=0; n_note<notes_qty; n_note++){
		
		n_length=adt_sheet_get_length(sheet,n_note);
		it=adt_sheet_get_start_t(sheet, n_note);
		aux_start_time=(int)floor(it*sampling_frequency);
		aux_f=n_length+decay_time;
		for (t=0, sample_shift=0; t<aux_f; t+=(float)period, sample_shift++){
		aux_samples[aux_start_time+sample_shift]+=gen_mod_wave_sample (synth, sheet, t, n_note);
		}

	}	
	aux_max_amp=0;
	aux_min_amp=0;
	amp_factor=0;
	for (sample=0; sample<samples_qty; sample++){
		if (aux_samples[sample]<aux_min_amp) aux_min_amp=aux_samples[sample];
		if (aux_samples[sample]>aux_max_amp) aux_max_amp=aux_samples[sample];
	}

	int_limit=(pow(2, BITS_PER_SAMPLE))/2-AMP_OFFSET;
	if (aux_min_amp+aux_max_amp>0 && aux_max_amp!=0){
		amp_peak=aux_max_amp;
		amp_factor=int_limit/amp_peak;
	}	
	if (aux_min_amp+aux_max_amp<0 && aux_min_amp!=0){
		amp_peak=abs(aux_min_amp);
		amp_factor=int_limit/amp_peak;
	}

	if((status=adt_wav_new(&wav,aux_samples,samples_qty,sampling_frequency,amp_factor))!=OK){
		adt_synth_destroy(&synth);
		adt_sheet_destroy(&sheet);
		free(aux_samples);
		fclose(f1);
		fclose(f2);
		fclose(f3);
		print_msg(status);
		if(status==ERROR_INVALID_DATA)fprintf(stderr,"%s\n",argv[o_arg_position]);
		return EXIT_FAILURE;
	}
	
	adt_wav_save(wav,f3);
	
	adt_wav_destroy(&wav);
	adt_sheet_destroy(&sheet);
	adt_synth_destroy(&synth);
	free(aux_samples);
	fclose(f1);
	fclose(f2);
	fclose(f3);
	
	print_msg(OK);
	
	return EXIT_SUCCESS;
}

void print_msg(status_t status){
	switch(status){
		case ERROR_NULL_POINTER:
			fprintf(stderr,"%s\n", s_msg[error_pointer]);
			break;
		case ERROR_MEMORY:
			fprintf(stderr,"%s\n", s_msg[error_mem]);
			break;
		case ERROR_INVALID_DATA:
			fprintf(stderr,"%s\n", s_msg[error_file_content]);
			break;
		case ARG_ERROR:
			fprintf(stderr,"%s\n", s_msg[error_arg]);
			break;
		case OK:
			printf("%s\n", s_msg[successfully]);
			break;
	}
	
}

status_t validate_args(int argc, char * argv[],unsigned int * sampling_frequency,unsigned int * s_arg_position,unsigned int * p_arg_position,unsigned int * o_arg_position){
	
	status_t arg_status[4];
	unsigned int aux;
	char * p_aux_char;
	size_t j, k;
	
	arg_status[0]=ARG_ERROR;
	arg_status[1]=ARG_ERROR;
	arg_status[2]=ARG_ERROR;
	arg_status[3]=ARG_ERROR;
	if (argc!=7 && argc!=9){
		fprintf(stderr,"%s\n",s_msg[error_arg]);
		return ARG_ERROR;	
	}
	for (j=1; j<argc; j+=2){
		if (!strcmp(argv[j],FREQ_CLA) && argv[j+1][0]!=STR_END){
			aux=strtol(argv[j+1], &p_aux_char, 10);
			if (* p_aux_char!=STR_END) {
				fprintf(stderr,"%s\n",s_msg[error_arg]);
				return ARG_ERROR;
			}
			for (k=0; k<VALID_F_QTY; k++){
				if (aux==frec[k]){
					*sampling_frequency=aux;
					arg_status[0]=OK;
				}
			}
			if (arg_status[0]!=OK) {
                                fprintf(stderr,"%s\n",s_msg[error_arg]);
                                return ARG_ERROR;
                        }
		}
		if (!strcmp(argv[j],SHEET_CLA) && argv[j+1][0]!=STR_END){
			arg_status[1]=OK;
			*p_arg_position=j+1;
		}
		if (!strcmp(argv[j],SYNTH_CLA) && argv[j+1][0]!=STR_END){
			arg_status[2]=OK;
			*s_arg_position=j+1;			
		}
		if (!strcmp(argv[j],OUTPUT_CLA) && argv[j+1][0]!=STR_END){
			arg_status[3]=OK;
			*o_arg_position=j+1;			
		}
	}
	if (!strcmp(argv[*p_arg_position],argv[*s_arg_position]) || !strcmp(argv[*p_arg_position],argv[*o_arg_position]) || !strcmp(argv[*s_arg_position],argv[*o_arg_position])) {
		fprintf(stderr,"%s\n",s_msg[error_file_repeat]);
		return ARG_ERROR;
	}
	if (arg_status[1]==ARG_ERROR || arg_status[2]==ARG_ERROR || arg_status[3]==ARG_ERROR) {
		fprintf(stderr,"%s\n",s_msg[error_arg]);
		return ARG_ERROR;
	}
	if (*sampling_frequency==0) *sampling_frequency=DEFAULT_F;
	return OK;
}
